﻿Imports System.Collections
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Reflection

Public Class uscListView
    Inherits ListView

    Public Sub New()
        Me.ListViewItemSorter = _Sorter
    End Sub

#Region "Sorting"
    Private _Sorter As New ListViewColumnSorter

    Protected Overrides Sub OnColumnClick(ByVal e As System.Windows.Forms.ColumnClickEventArgs)
        MyBase.OnColumnClick(e)

        _Sorter.SortType = Columns(e.Column).Tag

        If e.Column = _Sorter.SortColumn Then
            _Sorter.Order = If(_Sorter.Order = 1, 2, 1)
        Else
            _Sorter.SortColumn = e.Column
            _Sorter.Order = SortOrder.Ascending
        End If

        SetSortIcon(e.Column, _Sorter.Order)
        Sort()
    End Sub

    Private Class ListViewColumnSorter
        Implements IComparer

        Private ObjectCompare As CaseInsensitiveComparer
        Public SortColumn As Integer
        Public Order As SortOrder
        Public SortType As String

        Public Sub New()
            SortColumn = 0
            Order = SortOrder.None
            ObjectCompare = New CaseInsensitiveComparer()
        End Sub

        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
            Dim compareResult As Integer
            Dim listviewX As ListViewItem, listviewY As ListViewItem
            listviewX = DirectCast(x, ListViewItem)
            listviewY = DirectCast(y, ListViewItem)
            Dim VX As Object
            Dim VY As Object
            If listviewX.SubItems.Count > SortColumn Then
                VX = listviewX.SubItems(SortColumn).Text
            Else
                VX = "0"
            End If
            If listviewY.SubItems.Count > SortColumn Then
                VY = listviewY.SubItems(SortColumn).Text
            Else
                VY = "0"
            End If
            Select Case SortType
                Case "Number"
                    compareResult = DoubleCompare(CDouble(VX), CDouble(VY))
                Case Else
                    compareResult = ObjectCompare.Compare(VX, VY)
            End Select
            Select Case Order
                Case SortOrder.Ascending : Return compareResult
                Case SortOrder.Descending : Return -compareResult
            End Select
            Return 0
        End Function
    End Class

    Public Sub ClearSort()
        _Sorter.Order = SortOrder.None
    End Sub

    Public Sub SetSortIcon(ByVal columnIndex As Integer, ByVal order As SortOrder)
        Dim columnHeader As IntPtr = SendMessage(Me.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero)
        For columnNumber As Integer = 0 To Me.Columns.Count - 1
            Dim columnPtr = New IntPtr(columnNumber)
            Dim item = New HDITEM() With {.mask = HDITEM._Mask.Format}

            If SendMessage(columnHeader, HDM_GETITEM, columnPtr, item) = IntPtr.Zero Then
                Throw New Win32Exception()
            End If

            If order <> SortOrder.None AndAlso columnNumber = columnIndex Then
                Select Case order
                    Case SortOrder.Ascending
                        item.fmt = item.fmt And Not HDITEM.Format.SortDown
                        item.fmt = item.fmt Or HDITEM.Format.SortUp
                        Exit Select
                    Case SortOrder.Descending
                        item.fmt = item.fmt And Not HDITEM.Format.SortUp
                        item.fmt = item.fmt Or HDITEM.Format.SortDown
                        Exit Select
                End Select
            Else
                item.fmt = item.fmt And Not HDITEM.Format.SortDown And Not HDITEM.Format.SortUp
            End If

            If SendMessage(columnHeader, HDM_SETITEM, columnPtr, item) = IntPtr.Zero Then
                Throw New Win32Exception()
            End If
        Next
    End Sub
#End Region
End Class

Module ListViewExtension
    <StructLayout(LayoutKind.Sequential)>
    Public Structure HDITEM
        Public mask As _Mask
        Public cxy As Integer
        <MarshalAs(UnmanagedType.LPTStr)>
        Public pszText As String
        Public hbm As IntPtr
        Public cchTextMax As Integer
        Public fmt As Format
        Public lParam As IntPtr
        Public iImage As Integer
        Public iOrder As Integer
        Public type As UInteger
        Public pvFilter As IntPtr
        Public state As UInteger

        <Flags()>
        Public Enum _Mask
            Format = &H4 ' HDI_FORMAT
        End Enum

        <Flags()>
        Public Enum Format
            SortDown = &H200 ' HDF_SORTDOWN
            SortUp = &H400 ' HDF_SORTUP
        End Enum
    End Structure

    Public Const LVM_FIRST As Integer = &H1000
    Public Const LVM_GETHEADER As Integer = LVM_FIRST + 31

    Public Const HDM_FIRST As Integer = &H1200
    Public Const HDM_GETITEM As Integer = HDM_FIRST + 11
    Public Const HDM_SETITEM As Integer = HDM_FIRST + 12

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As UInt32, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As UInt32, ByVal wParam As IntPtr, ByRef lParam As HDITEM) As IntPtr
    End Function
End Module